Skip to content

Standardize Python packaging and isolate bundled MLIR - #1016

Open
mouliangyu wants to merge 32 commits into
hw-native-sys:mainfrom
mouliangyu:codex/standardize-python-packaging-main
Open

Standardize Python packaging and isolate bundled MLIR#1016
mouliangyu wants to merge 32 commits into
hw-native-sys:mainfrom
mouliangyu:codex/standardize-python-packaging-main

Conversation

@mouliangyu

Copy link
Copy Markdown
Collaborator

背景

PTOAS 原先通过自定义 PEP 517 backend、setup.py、wheel bootstrap、运行时路径扫描和多套发布脚本维护 Python 包。这些代码重复承担了标准打包工具已有的 metadata、editable install、console script、wheel 组装和 native dependency repair,也使 CLI、PTODSL、CI、wheel 和 standalone archive 显式依赖源码树、LLVM build tree 或旧 install tree 的内部布局。

同时,直接发布顶层 mlir 包会与进程中的其他 MLIR Python distribution 冲突。PTOAS 需要拥有一套自洽的 MLIR Python runtime,并让 compiler native module 与 Python bindings 共享同一套 MLIR CAPI。

本 PR 将 Python 构建与发布链路收敛到 scikit-build-core、AddMLIRPython 和标准平台 repair 工具,并把 PTOAS 自带的 MLIR runtime 隔离到 ptoas.mlir

修改内容

  • 使用 scikit-build-core 作为标准 PEP 517/660 backend:
    • 根项目发布 ptoas
    • packaging/ptoas-vmi 发布 ptoas-vmi
    • 两个 distribution 共享 CMake 构建图、Python package 和 console script;
    • 支持标准 wheel 与 editable install,删除自定义 backend、setup.py 和 wheel bootstrap。
  • 将 PTOAS 自带的 MLIR Python runtime 放到项目命名空间:
    • public import 改为 ptoas.mlirptoas.mlir.dialects.pto
    • 设置 MLIR_PYTHON_PACKAGE_PREFIX=ptoas.mlir. 和独立 nanobind domain;
    • 不再安装或兼容 alias 到顶层 mlir
  • 使用 AddMLIRPython 的声明式 source graph 构建 MLIR Python package:
    • 显式选择 PTOAS/PTODSL 使用的 upstream dialect;
    • 使用标准 site initializer 注册 dialect;
    • 保留一个 package-owned PTOASPythonCAPI
    • 仅对上游 MLIR/nanobind 生成源码局部免除 -Werror,PTOAS 自有代码继续保持 -Werror
  • 将 native compiler 统一为 ptoas._core
    • LLVM driver sources 作为 object library 保持 LLVM 的 RTTI/exception 编译模式;
    • Python binding translation units 保持 pybind11 所需的 RTTI/exception;
    • CLI 和 PTODSL 通过正常 Python import 使用 _core,不再扫描 native library 路径。
  • 统一 CLI 与运行时路径契约:
    • wheel console script 使用 ptoas._cli:main
    • build/install/archive wrapper 只添加各自 tree 对应的 Python root;
    • PTODSL 查找编译器时只接受显式 PTOAS_BIN,否则使用标准 PATH
    • 删除 _bootstrap.py_runtime_entry 和跨源码/build/install tree 的路径猜测。
  • 标准化版本与 release 流程:
    • ptoas-vmi 的 PEP 621 version 是 VMI distribution 的唯一版本源;
    • scikit-build-core 通过 SKBUILD_PROJECT_VERSION_FULL 将版本传给 CMake/CLI;
    • 删除 PTOAS_VMI_VERSION 扫描、同步脚本和自动 patch bump;
    • Linux wheel 使用 auditwheel repair,macOS wheel 使用 delocate-wheel
    • payload 校验按平台识别 .so/.dylib common CAPI。
  • 标准化开发环境与 CI:
    • quick_install.sh 使用标准 editable install;
    • CI 与 simulator CI 使用 workflow-owned venv 和 editable install;
    • lit 从 CMake 获取匹配的 MLIR Python package;
    • editable native targets 显式使用外部 LLVM build RPATH,wheel 保持 package-relative RPATH 并交给 repair 工具处理;
    • wheel payload、isolated install、import、CLI 和编译流程由 workflow 验证。
  • 重构 standalone compiler archive:
    • PTOAS_PythonPTOAS_CompilerArchive 两个 CMake component 组装受控 install tree;
    • install-time deployment 使用 CMake target 元数据确定 _corePTOASPythonCAPI
    • 使用 file(GET_RUNTIME_DEPENDENCIES) 收集外部依赖并检查 unresolved/ABI 冲突;
    • Linux 生成 archive-relative $ORIGIN RPATH,macOS 使用 delocate-path
    • 删除原有 Linux/macOS shell dependency collectors。
  • 同步更新 README、构建文档、设计文档、PTODSL/TileLang imports、samples 和测试。

验证

在重放到最新 main 后:

  • PEP 517 metadata:ptoas 0.53ptoas-vmi 0.1.4
  • 四个 workflow YAML 均可解析,旧 VMI version 脚本/变量无残留引用。
  • VMI editable install 增量构建成功;清除 LD_LIBRARY_PATH/PYTHONPATH 后:
    • ptoas.mlir.ir、PTO dialect 和 ptoas._core 可导入;
    • ptoas --version 输出 ptoas vmi 0.1.4
  • 实际 repaired VMI wheel payload 校验通过。
  • git diff --check 通过。
  • PR386 license checker 检查 273 个变更源码/脚本文件,全部通过。

同一变更在重放前的 release dry-run 中完成了 Linux 全矩阵验证:

  • Python 3.10/3.11/3.12 × x86_64/aarch64,共 6 个 ptoas_vmi-0.1.4 wheel;
  • 全部通过 build、payload validation、auditwheel repair、隔离安装测试和 artifact 上传;
  • 额外下载 Python 3.11 x86_64 wheel,在本地新 venv 中清除外部库路径后安装、import 和 CLI 验证通过。

范围说明

  • ptoasptoas-vmi 会安装相同的顶层 Python package 和 console script,因此两个 distribution 互斥,不应装入同一环境。
  • macOS release dry-run 已验证修复后的 .dylib payload validation,并进入 delocate repair;完整 macOS matrix 由本 PR 的 workflow 继续覆盖。

@mouliangyu
mouliangyu marked this pull request as ready for review July 27, 2026 17:44
@mouliangyu
mouliangyu force-pushed the codex/standardize-python-packaging-main branch 2 times, most recently from 94ca0cc to 0acf08e Compare July 28, 2026 03:39
@mouliangyu

Copy link
Copy Markdown
Collaborator Author

[P1] The ptoas-vmi sdist is not self-contained. pyproject.toml (line 44) references CMake and packages through ../... Building the sdist produced only pyproject.toml and PKG-INFO; rebuilding a wheel from it failed because the referenced CMake source tree was absent. Any source installation or package-index sdist fallback will fail. The VMI project must stage/include its complete sources and use paths internal to the sdist.
[P1] TileLang text-to-module conversion imports the removed package. lowering_backend.py (line 77) still imports pto.dialects.pto. The new distributions install only ptoas, ptodsl, and tilelang_dsl, so LoweringResult(text=...).as_module() raises ModuleNotFoundError in a clean install. It should import ptoas.mlir.dialects.pto. Existing tests pass because none calls as_module() on a text result.
[P2] The repository build skill still validates the deleted package layout. SKILL.md (line 128) expects _pto.cpython-*.so and $PTO_INSTALL_DIR/mlir/dialects/pto.py, then configures PYTHONPATH around LLVM’s top-level mlir_core. This PR now produces ptoas/_core and ptoas/mlir, so the documented workflow fails its output checks and contradicts the isolation contract.

@mouliangyu

Copy link
Copy Markdown
Collaborator Author

已在 0616b6dcd 修正这三处问题:

  1. VMI 源码包不自包含

    • 删除了 packaging/ptoas-vmi/pyproject.toml 这份依赖 ../.. 的重复配置,根目录 pyproject.toml 仍是唯一的完整构建配置。
    • VMI wheel 构建时,prepare_source.py 通过 git archive 导出完整的已跟踪源码树,再对 staging 中的根 pyproject.toml 应用一份只包含 VMI distribution 差异的 patch(包名、静态版本、CLI label 等)。wheel 直接从这个 staging 目录构建,不再引用源码树外路径,也不需要手工枚举 CMake/源码目录。
    • 这段 staging 逻辑仅在 vmi-v* release 或手动选择 release_kind=vmi 时执行;普通 PR/push 不执行 VMI staging。sdist 不纳入发布物或门禁,也没有新增 PyPA build 依赖。
    • 本地验证 staging 后元数据为 ptoas-vmi 0.1.4,关键源码/CMake 文件齐全,pyproject.toml../。另外独立做过一次完整的 sdist -> 解包 -> wheel 验证,解包后的源码可单独构建,证明 staging 本身是自包含的。
  2. TileLang 仍引用旧命名空间

    • 已改为 ptoas.mlir.dialects.pto
    • 删除了对 func/arith/scf 的显式注册;这些 bundled upstream dialect 已由 PTOAS MLIR site initializer 注册,只显式注册项目自有的 PTO dialect。
    • 增加了直接调用 LoweringResult(text="module {}").as_module() 的回归测试,本地通过(1 test)。
  3. WSL skill 与当前包结构不一致

    • 已更新为 LLVM 21、quick_install.shptoas._coreptoas.mlir 的当前安装/导入约定,移除了旧 _pto 与顶层 mlir 布局描述。
    • skill validator 本地通过。

补充验证:两个 workflow YAML 均可解析;相关 Python 文件 py_compile 通过;VMI metadata patch 的 git apply --check 通过;git diff --check 通过。

@mouliangyu
mouliangyu force-pushed the codex/standardize-python-packaging-main branch 2 times, most recently from 42e9c8e to dfad6ce Compare July 29, 2026 04:53
@Zhendong404

Copy link
Copy Markdown
Collaborator

Review:缺陷与风险汇总

整体方向(收敛到 scikit-build-core、隔离 ptoas.mlir、统一 _core)是合理的,一致性检查大体通过:无残留顶层 mlir import、无对已删模块的引用、install component 命名同步。但有以下问题,按严重程度排列:

高(建议合入前处理)

  1. docker/Dockerfile builder 阶段大概率构建失败
    Dockerfile:60-71 仍设 PTO_WHEEL_DIST_DIRtest_wheel_imports.sh:36-39 会优先选中未经 auditwheel repair 的原始 wheel 做 import 测试;原始 wheel 不带 libLLVM/libMLIR,而 LD_LIBRARY_PATH 只在 repair 那条 RUN 内有效。workflow 里测的是 repair 后 wheelhouse 的 wheel,所以 CI 绿不代表 docker build 能过。建议改为 PTO_TEST_WHEEL_PATH=/wheelhouse/ptoas*.whl bash ... 或取消该 ENV。

  2. cmake/RelocatePTOASCompilerArchive.cmake.in 对 unresolved 依赖直接 FATAL_ERROR
    file(GET_RUNTIME_DEPENDENCIES) 在 Linux 上 linux-vdso.so.1 永远落在 unresolved 列表(脚本 ~L104-111 未豁免),cmake --install --component PTOAS_CompilerArchive 很可能直接报 "Unresolved ... linux-vdso.so.1" 失败。需先过滤已知系统库再报错。

  3. Wheel CLI 丢失 PYTHONPATH shadow 防护
    被删的 ptoas_wheel_bootstrap.py 的存在意义就是防止外部 PYTHONPATH 中同名 ptoas 包遮蔽 wheel(曾有明确报错 + 319 行测试)。新 ptoas/_cli.py 完全遵循标准包优先级,而 scripts/ptoas_env.sh 仍会 prepend build/install 树路径——开发者 source 后运行 wheel 版 ptoas静默混用两套代码。同时 test_wheel_imports.sh 的污染环境测试也被删。设计文档表明这是有意取舍,但至少应检测 ptoas.__file___core.__file__ 是否同源并告警。

  4. 新测试全部 mock 掉 _core 加载,无真实冒烟
    test_ptoas_cli.py 所有用例都 patch 了 _load_native_module。若 wheel 内 RPATH 配置有误,测试全绿但 ptoas --version 直接 ImportError。旧的真子进程 wheel 冒烟(test_ptoas_wheel_bootstrap.py 中 reexec 用例)被删后无等价替代。这是本次重构最核心的路径,恰恰没有覆盖,建议补一个不经 mock 的最小冒烟测试。

  5. LLVM cache 保护被删
    两个 wheel workflow 的 save 条件从 cache-hit != 'true' && event != pull_request && event != release 改成仅 cache-hit != 'true'。多个 PR 在同一 LLVM SHA 同时未命中时会争写同一 key,cache/save 在 key 已存在时报错,PR 门禁会随机红。建议恢复事件过滤或加 continue-on-error

  1. resolve_ptoas_binary 删除仓库 build/install 树回退test_docs_as_test.pytest_ptoas_frontend_verify.py 等在 ctest 外直接运行从通过变 FileNotFoundError。若是有意的行为收缩请在 PR 描述说明。
  2. python/pto/dialects/pto.py 强依赖完整 _core:只构造 PTO IR 也要加载整个编译器 driver,且旧代码的候选路径诊断丢失,失败只剩裸 ImportError。
  3. test_context.py 明显削弱:旧测试验证 sys.path 纯净 + pto/llvm 双 dialect 加载;新版只有 assertIsNotNone,而 _context.py 对 llvm dialect 是 except Exception: None 静默吞错——llvm 加载失败测试永远绿。
  4. macOS 矩阵新增 x86_64+py3.10:原 exclude 被无条件删除,若 runner 镜像不提供该组合 release 会挂,请确认已验证。
  5. VMI 版本成硬编码单点:自动 bump 脚本被删,版本只存在于 pyproject.toml.patchprepare_source.py 只在 vmi 分支路径执行,普通 PR 门禁不校验。建议 CI 加一步 prepare_source.py --print-version 廉价校验。
  6. 死环境变量残留PTO_PYTHON_ROOT/PTO_PYTHON_BUILD_ROOTptoas_env.sh:56-57、README:207-208、ci-board-validation-guide.md 仍在指导用户设置)与 MLIR_PYTHON_ROOTDockerfile.dev:78,还指向旧顶层 mlir 布局)的唯一消费者已被删,会误导用户。
  7. _resolve_wrapper_pathshutil.which 回退:argv[0] 为裸命令名时直接 SystemExit;python -m ptoas._cli 会把 .py 源文件路径设为 PTOAS_BIN,后续报令人困惑的错误。
  8. _cli.launch() 无条件覆盖 PTOAS_BIN/PTOAS_PYTHON_EXE,建议 setdefault 或只对子进程传 env。

  1. PTOAS_DEFAULT_* 把 CI 构建机绝对路径烘进 wheel 的 _core:直接调 _core.main() 会落到不存在的构建机路径,且泄露构建机路径。wheel 构建建议显式置空。
  2. wheel 版本号无法再覆盖(regex provider 只认 CMakeLists project(... VERSION 0.54),且只接受两段版本号);PTOAS_RELEASE_VERSION_OVERRIDE 只改 CLI 字符串,会造成 CLI 与 importlib.metadata 版本不一致。
  3. 异常跨 RTTI/EH 边界:driver 以 -fno-exceptions 编译,异常逃逸穿过无异常表的帧会 std::terminate,建议 runPTOASFromPythontry/catch(...) 兜底转为 Python 异常。
  4. docker/test_ptoas_cli.sh 成孤儿文件(全树无调用方)。
  5. pto.py 仍未导出 C++ 侧已绑定的 HiF8x2Type/F8E8M0Typeptodsl/_types.py:463 引用会 AttributeError;merge-base 已存在,但本次重写了导出清单,建议顺手修)。
  6. validate_wheel_payload.py 只在 workflow 里跑,本地无 pytest/ctest 入口。

需作者确认的行为变更

  • editable redirect 模式下 import ptoas._core 是否可靠(finder 可能把 ptoas.__path__ 锁定到源码目录,依赖 scikit-build-core 隐式行为),建议在 PR 中明确验证记录。
  • editable install 与 wheel 是两套 RPATH 代码路径,ci_sim 绿不代表发布 wheel 在相同场景可用,属知情取舍的话请写明。

建议优先修 1、2、5(确定性故障)并补 4 的真实冒烟测试,其余可作 follow-up。

@mouliangyu
mouliangyu force-pushed the codex/standardize-python-packaging-main branch from 44ef28c to 59396a9 Compare July 29, 2026 06:43
@mouliangyu

mouliangyu commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

已按这轮意见在 a4f3efda956e34cf0998ac5bc51 修正:

  1. Docker builder 现在只验证 repair 后的 wheel

    • builder 中 build/wheel-dist 保留的是 PEP 517 产出的 raw wheel,/wheelhouseauditwheel repair 后可交付给 runtime stage 的 wheel。
    • docker/test_wheel_imports.sh 现在通过 PTO_TEST_WHEEL_PATH=/wheelhouse/ptoas*.whl 明确重装并验证 repaired wheel,不再因 PTO_WHEEL_DIST_DIR 误选 raw wheel。
    • 同时删除了 smoke 后的 ENV PATH=$PTO_BUILD_DIR/tools/ptoas:$PATH;后续 which ptoas 和 sample 编译继续验证 wheel 安装的 console entry,不会切回 CMake build-tree wrapper。
  2. 第 11 条:legacy 环境逻辑已清理

    • 删除 scripts/ptoas_env.sh,移除 TileLang CI、编译脚本和文档中的 source 调用。
    • 普通仓库流程统一使用当前 Python 环境由 wheel/editable install 提供的 ptoas;需要覆盖时使用 PTOAS_BINrun_st.pytest/samples/runop.sh 不再自动探测仓库 build/tools/ptoas/ptoas
    • CANN/Bisheng/PTO-ISA 环境仍由 CANN setenv 或对应显式参数负责;全树已无 PTO_PYTHON_ROOTPTO_PYTHON_BUILD_ROOTMLIR_PYTHON_ROOTPTOAS_ENV_SKIP_SMOKE_TEST 残留。
    • .codex/skills 中专门验证 CMake build 产物的显式 build-tree 路径保留,这是内部构建验证,不是普通安装入口。
  3. 第 18 条:Python 类型导出已补齐

    • ptoas.mlir.dialects.pto 现在导出 HiF8x2TypeF8E8M0Type,并加入 __all__
    • 扩充 test/python/low_precision_types.py,由 CTest 直接覆盖两种类型;本地 staged package 实测得到 !pto.hif8x2!pto.f8E8M0
    • 这不是 rebase 解冲突时误删:merge-base、rebase 前分支、当前 main 都只有 C++ 绑定而缺 Python re-export;是历史导出清单遗漏,本次补齐。

验证:shell 脚本 bash -n、相关 Python py_compilefind_ptoas_bin() PATH/override 行为检查、PTOASPythonPackage 构建、ctest -R '^pto_low_precision_types$'、直接类型 import/构造、git diff --check 均通过。

@mouliangyu

Copy link
Copy Markdown
Collaborator Author

补充:新 head 的 vpto-sim-validation 实际失败在 TileLang shared build,而非 wheel:run_all_st.py --jobs 64--jobs 只控制 build 后的 testcase 执行,共享 CMake build 仍无条件使用 os.cpu_count(),在 64 核 runner 上同时拉起 64 个 ptoas + PTODSL daemon,日志中出现 13 次 daemon socket 未创建和 5 次 RPC child timeout。

已在 aa3c436b3 增加独立的 --build-jobs,CI 改为 --build-jobs 8 --jobs 64:共享 daemon-heavy build 限到 8 路,build 后的 simulator testcase 仍保留 64 路并发。8 与仓库现有 PTODSL parallel runner 的默认安全并发一致。相关 Python py_compile、参数 help、mock 验证 make ... -j 8、workflow YAML 解析和 git diff --check 均通过。

@Zhendong404

Copy link
Copy Markdown
Collaborator

二轮 review:以下两个高危项仍未处理

感谢修复 Dockerfile wheel 验证、死环境变量清理和低精度类型导出。rebase 后的 delta 我核对过,质量没问题。但一轮 review 中的这两个高危项在最新 head(07ddb4542)上原样未动,建议合入前处理:

1. 所有新测试都 mock 掉了 _core 加载,wheel 的真实加载路径零覆盖

ptodsl/tests/test_ptoas_cli.py 的全部用例都 patch 了 _cli._load_native_module(如 L36-38、L81-82),没有任何测试真实执行 from ptoas import _core

这意味着:如果 wheel 内的 RPATH 配置、bundled libLLVM/libMLIR 布局或 MLIR_PYTHON_PACKAGE_PREFIX 任何一处有误,这套测试全绿,但用户跑 ptoas --version 直接 ImportError。旧的真子进程 wheel 冒烟(test_ptoas_wheel_bootstrap.py 中的 reexec 用例)被删后没有等价替代。

这次重构把 CLI 的唯一入口换成了 ptoas._cli:main_core,这条路径恰恰是整个 PR 最核心的契约。建议补一个不经 mock 的最小冒烟:在装好 wheel 的干净 venv 里跑 ptoas --version(或至少真实 import ptoas._core 并调 main(["--version"])),挂到 CI 的 wheel 测试步骤里。

2. LLVM cache save 仍有并发竞态,PR 门禁会随机红

build_wheel.yml:199-201build_wheel_mac.yml 同)的 save 条件是 cache-hit != 'true',没有事件过滤也没有 continue-on-error

从 commit 标题看"允许 PR 写 cache"是有意决策,这点没问题。但竞态依然存在:多个 PR(或 PR 与 nightly)在同一个 LLVM SHA 上同时缓存未命中时,都会执行 actions/cache/save@v4 写同一个 key llvm-<sha>-manylinux_2_34-<arch>-<python>-<flavor>cache/save 在 key 已存在(或另一 job 正在 reserve)时会报错并使该步骤失败——而后提交的 PR 并没有做错任何事,门禁却红了,retry 也未必能过(取决于谁先完成 reserve)。

建议至少给 save 步骤加 continue-on-error: true(cache 写不进去只损失下次的构建时间,不应 fail 门禁),或恢复 PR 只读、由 main/nightly 独占写 cache。

@mouliangyu

Copy link
Copy Markdown
Collaborator Author

这两条我结合当前 head 的 workflow、脚本和实际门禁日志重新核对了,当前都已有覆盖或保护,不需要再改代码。

  1. 真实 wheel → _core → CLI 路径已经由 wheel integration smoke 覆盖

    • ptodsl/tests/test_ptoas_cli.py mock _load_native_module 是有意只验证 Python launcher 的参数和资源路径拼装,不承担 wheel/RPATH 集成验证。
    • 集成验证在 build_wheel.ymlTest wheel installation:它把 auditwheel repair 后的 wheelhouse/ptoas*.whl 通过 PTO_TEST_WHEEL_PATH 传给 docker/test_wheel_imports.sh
    • 该脚本会新建临时 venv、安装该 wheel、切到 /tmp、清除 PYTHONPATH,随后真实执行:
      • from ptoas import _core(脚本 L102);
      • ptoas --version(L103);
      • env -i ... <installed-entrypoint> --version(L123-128);
      • 最后还用同一个 installed entrypoint 编译 PTODSL 生成的 PTO IR。
    • 最新 head 的 x86_64 job 日志明确显示 _core 来自临时 venv 的 repaired wheel,普通环境和 clean environment 都输出 ptoas 0.55,最终 All wheel import tests passed!
      https://github.com/hw-native-sys/PTOAS/actions/runs/30437778922/job/90529568364
    • aarch64 job也完成了同一条真实路径:
      https://github.com/hw-native-sys/PTOAS/actions/runs/30437778922/job/90529568314
  2. actions/cache/save@v4 的同 key 竞争只告警,不会使 step 失败

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants